#!/usr/bin/env bash

# Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.

set -o errexit
set -o nounset

script_name=$(basename $0)

function usage()
{
  echo "MFEM CI build and test driver"
  echo ""
  echo "Syntax:"
  echo "> ${script_name} --spec \"spack spec\" [--deps-only] [--data]"
  echo "                 [--data-dir=/path/to/mfem/data]"
  echo ""
  echo "> ${script_name} --build-only [--data]"
  echo "                 [--data-dir=/path/to/mfem/data]"
  echo ""
  echo "> ${script_name} --test-only [--data]"
  echo "                 [--data-dir=/path/to/mfem/data]"
  echo ""
  echo "Options:"
  echo " --spec"
  echo "    The most common usage is to pass a spack spec to the script."
  echo "    This spec may or may not specify \"mfem\" as the package, since it"
  echo "    is the default."
  echo ""
  echo " --deps-only"
  echo "    Only install MFEM dependencies. Will clone mfem-uberenv to drive a"
  echo "    local instance on spack with which we install the dependencies."
  echo "    This local spack instance is tuned to be completely independent"
  echo "    from the user environment."
  echo ""
  echo " --build-only"
  echo "    Only build MFEM, based on the existing configuration."
  echo ""
  echo " --test-only"
  echo "    Only test MFEM, based on the existing configuration."
  echo ""
  echo " --data"
  echo "    Make running tests with \"--data\" a requirement: will fail in the"
  echo "    data directory is not present in the parent of the mfem root directory."
  echo "    Note: default behavior is to run data tests if data dir is present."
  echo ""
  echo " --data-dir=/path/to/mfem/data"
  echo "    Path to a clone of the MFEM/data repo: https://github.com/mfem/data"
  echo "    The default path is: '../data'."
  echo ""
}

project_dir="$(pwd)"

mode=""
spec=""
data_dir=""
with_data=false

sys_type=${SYS_TYPE:-""}

threads=${THREADS:-""}

# Options
while [[ $# -gt 0 ]]
do
    key="$1"

    case $key in
        --spec)
            spec="$2"
            shift # past argument
            shift # past value
            ;;
        --deps-only|--build-only|--test-only)
            mode="$key"
            shift # past argument
            ;;
        --data)
            with_data=true
            shift # past argument
            ;;
        --data-dir)
            data_dir="$2"
            shift # past argument
            shift # past value
            ;;
        -h|--help)
            usage
            exit 0;
            ;;
        *)  # unknown option
            echo "ERROR: option $key is unknown"
            exit 1;
            ;;
    esac
done

# Dependencies
if [[ "${mode}" != "--build-only" && "${mode}" != "--test-only" ]]
then
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "~~~~~ Building Dependencies"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    if [[ -z ${spec} ]]
    then
        echo "--spec is mandatory to build dependencies, aborting..."
        exit 1
    fi

    upstream_opt=""
    if [[ ${sys_type} != "" ]]
    then
        upstream_opt="--upstream=/usr/workspace/mfem/mfem-dependencies/install"
    fi

    prefix_opt=""

    # Do not run on /dev/shm if only installing dependencies: they will be lost
    # otherwise.
    if [[ -d "/dev/shm" && "${mode}" != "--deps-only" ]]
    then
        prefix="/dev/shm/${USER}_${CI_PIPELINE_ID:-"NONE"}_${RANDOM}"
        mkdir -p ${prefix}
        # clean up ${prefix} at exit:
        trap 'rm -rf "${prefix}"' EXIT
        prefix_opt="--prefix=${prefix}"
    fi
    echo ${spec} > spec.txt

    echo "Fetching uberenv."
    tests/gitlab/get_mfem_uberenv || { echo "Error fetching Uberenv"; exit 1; }

    echo "Removing existing configuration"
    make distclean

    # Add -O2 flag for debug builds to speed up the tests.
    sed -e 's/^\(DEBUG_FLAGS =.*\)$/\1 -O2/g' config/defaults.mk > \
        config/defaults.mk.new
    mv -f config/defaults.mk.new config/defaults.mk

    echo "Installing dependencies."
    command -v python > /dev/null && python_cmd=python || python_cmd=python3
    $python_cmd tests/uberenv/uberenv.py --spec="${spec}" "${upstream_opt}" \
        "${prefix_opt}"

    # Make sure that a configuration was generated by spack (part 1).
    cp config/config.mk config/spack-config.mk
    cp config/_config.hpp config/spack_config.hpp
fi

# Configuration
if [[ "${mode}" != "--deps-only" ]]
then
# Host config file
# Make sure that a configuration was generated by spack (part 2).
# Without this running the rest of the script would generate a new config.
    if [[ -f "config/spack-config.mk" && -f "config/spack_config.hpp" ]]
    then
        cp config/spack-config.mk config/config.mk
        cp config/spack_config.hpp config/_config.hpp
    else
        echo "No result for at least one of"
        echo "    ${project_dir}/config/spack-config.mk"
        echo "    ${project_dir}/config/spack_config.hpp"
        echo "ERROR: Spack generated configuration not found."
        exit 1
    fi

    # Setup the MFEM/data repository directory
    # Some additional unit tests are enabled when '../data' is present
    if [[ -z "${data_dir}" ]]
    then
        # By default, data_dir is ../data.
        data_dir="../data"
    else
        # data_dir is specified, so we need to link its content into the
        # project parent dir.
        if [[ -e "../data" ]]; then
            if [[ -L "../data" ]]; then
                echo "'../data' link already exists. Deleting."
                rm "../data"
            else
                echo "Error: '../data' already exists and it's NOT a link"
                exit 1
            fi
        fi
        ln -sf "${data_dir}" "../data"
    fi
    # The PUMI examples expect the PUMI datafiles to be in 'data/pumi'
    if [[ -d "../data/pumi" ]]; then
        ln -sf "../../data/pumi" "data"
    fi

    if [[ "$with_data" == "true" && ! -d "../data" ]]
    then
        echo "ERROR: '$data_dir' is not a directory while asking for --data"
        exit 1
    fi
fi

# Build (also build when asked for testing, to make sure we test what we want)
if [[ "${mode}" != "--deps-only" ]]
then
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "~ Project Dir: ${project_dir}"
    echo "~ Data Dir: ${data_dir}"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "~~~~~ MFEM configuration"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    make info

    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "~~~~~ Building MFEM"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    make all -j ${threads}
fi

# Test
if [[ "${mode}" != "--deps-only" && "${mode}" != "--build-only" ]]
then
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "~~~~~ Testing MFEM"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    make test
    test_status=$?

    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
    echo "~~~~~ Cleaning MFEM"
    echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"

    if make distclean > distclean.log 2>&1; then
      echo "[make distclean]: OK"
    else
      cat distclean.log
      echo
      echo "[make distclean]: FAILED (see above)"
    fi
    rm -f distclean.log

    exit $test_status
fi
